/**
 * WTF Engine
 *
 * License... etc.
 **
 * WTF Network Protocol Implementation
 **
 * Author: Sean Micklethwaite
 **/

#ifndef __net_protocol_implH__
#define __net_protocol_implH__

#include "net_protocol.h"
#include "config.h"
#include "dator.h"
#include "statemachine.h"

#ifdef __NET_ASIO__
#	include "net_asio.h"
#endif

namespace WtfEngine {

	class Peer;
	class Party;

	/**
	 * The all-purpose packet wrapper.
	 **/
	template <class Type = PacketData>
	class Packet : public virtual IPacket, public GcObject {
		typename NonCopyBuffer<Type>::tRef	mrBuffer;

		public:
			/// Creates a new packet and buffer
			inline Packet()
				: mrBuffer( new NonCopyBuffer<Type>(new Type(), 1) )
			{};

			/// Creates a new packet and buffer
			inline Packet(const Type& data)
				: mrBuffer( new NonCopyBuffer<Type>(new Type(data), 1) )
			{};

			/// Construct from raw buffer type (explicitly).
			inline explicit Packet(const void * pBuf)
				: mrBuffer( new NonCopyBuffer<Type>((const Type *)pBuf, 1))
			{};
			/// Construct from another buffer type (explicitly).
			template <class T>
			inline explicit Packet(const typename NonCopyBuffer<T>::tRef& rBuf)
				: mrBuffer(rBuf)
			{};
			/// Construct from another packet type (explicitly).
			template <class T>
			inline explicit Packet(typename Packet<T>::tRefa r)
				: mrBuffer(r.mrBuffer)
			{};

			/// We can downcast from derived packet types implicitly
			template <class T>
			inline Packet(const Packet<T>& pkt)
				: mrBuffer(new NonCopyBuffer<Type>(pkt.mrBuffer))
			{
				static_cast<Type *>((T *)1);
			};

			// IPacket methods 
			unsigned getSize() const {
				return mrBuffer->getData()->PacketSize;
			};
			const unsigned char * getData() const {
				return (const unsigned char *)(mrBuffer->getData());
			};
			Type * getPacket() {
				return mrBuffer->getData();
			};
			const Type * getPacket() const {
				return mrBuffer->getData();
			};
			
			DELEGATE_OPERATORS_IN_TPL(Type, *getPacket());
			GC_AUTOSIZE(Packet);
	};


	/**
	 * Base class for peer state machine
	 **/
	class PeerState: public WtfEngine::State {
	public:
		PeerState(): WtfEngine::State(Kernel::GetInstancePtr()) {};
		virtual ~PeerState() {};

		virtual void ProcessPeer(Peer * pPeer) {};
		virtual void ProcessMessage(Peer * pPeer, Packet<MessagePacket>::tRefa rPacket);

		GC_AUTOSIZE(PeerState);
	};

	/**
	 * Represents a connected peer
	 **/
	class Peer : public GcObject, public StateMachine<PeerState> {
		public:
			typedef enum {
				/// Peer has not yet responded to greeting
				UNIDENTIFIED = 0,

				/// Peer is not a part of the game, but traffic is routed through
				/// for matchmaking, etc.
				PASSIVE = 1,

				/// Peer is a normal client - this is the default
				PARTY_GUEST,

				/// Peer is a permanent member of the party
				PARTY_MEMBER,

				/// Peer has authority over the game; this can only be
				/// assigned by the master server or granted voluntarily
				PARTY_LEADER,

				/// Peer is the master (stats) server. Connection is initiated
				/// by this node.
				MASTER_SERVER,

				/// Peer has full access to the engine. This is available to peers
				/// on the local machine and by explicit user request; used by
				/// the editor and debugger.
				PRIVILEGED
			} tPeerType;

			typedef std::set<GameObject::tRef> tObjectSet;

		private:
			unsigned mNodeId;
			unsigned mMyNodeId;

			tPeerType		mPeerType;

			/// Peer's working set - only accept updates for objects in this set
			/// (or new objects)
			tObjectSet mWorkingSet;

			/// This TCP socket is used for essential control messages
			ISocket::tRef	mrControlSocket;
			/// This socket is used to send data, and can be TCP or UDP.
			ISocket::tRef	mrDataSocket;

			Packet<>::tRef mrNextPacket;

		public:
			Peer(unsigned id, ISocket::tRefa rControlSocket, tPeerType type);
			~Peer();

			virtual void Destroy();

			void SetDataPort(tPort port);
			
			void Process();
			void ProcessPacket(Packet<>::tRefa rPacket);
			void ProcessUpdate(const UpdatePacket * pUpdate);

			void SendControlPacket(Packet<>::tRefa rPacket);
			void SendDataPacket(Packet<>::tRefa rPacket);

			// Event handlers
			void OnRecvControlPacket();

			inline ISocket::tRef getControlSocket() const {
				return mrControlSocket;
			};

			GC_AUTOSIZE(Peer);
	};


	/**
	 * A party is a group of nodes which are arranging/playing a game.
	 **/
	class Party: public GcObject {
	public:
		typedef std::list<Peer::tRef> tPeerList;


	private:
		tPeerList   mvPeerList;
		
		tGameTime mTimeStamp;

		unsigned muLocalPartyId;
		unsigned muMinSize, muMaxSize, muGameId;
		int miGameType;

		public:
			Party(unsigned gameId, int gameType, unsigned minSize = 2, unsigned maxSize = 8)
			: GcObject(), muMinSize(minSize), muMaxSize(maxSize), muGameId(gameId), miGameType(gameType)
			{ };


			/// Tries to recruit more party members by broadcasting over the LAN, on
			/// the specified port.
			/// @pre	Party status is idle.
			void BroadcastOnLan(tPort port);

			/// Sends the packet to all connected peers
			void BroadcastToPeers(const IPacket::tRef& rPacket);

			inline void AddPeer(const Peer::tRef& rPeer) {
				mvPeerList.push_back(rPeer);
			};
			inline void RemovePeer(const Peer::tRef& rPeer) {
				mvPeerList.remove(rPeer);
			};


			PartyBroadcastMessage CreateBroadcastMessage() const;
			
			virtual Packet<PartyDetailsMessage>::tRef CreatePartyDetails() const;

			inline tGameTime getTimeStamp() const {
				return mTimeStamp;
			};

			GC_AUTOSIZE(Party);
	};

	class PeerManager: public Task<PeerManager> {
	public:
		/**
		 * Base class for manager state machine
		 **/
		class State: public WtfEngine::State {
		public:
			State(): WtfEngine::State(Kernel::GetInstancePtr()) {};
			virtual ~State() {};

			virtual void Process() {};
			virtual void ProcessMessage(Packet<MessagePacket>::tRefa rPacket);

			GC_AUTOSIZE(State);
		};

	private:
		/// The node's control socket (server port).
		TcpSocket::tRef mrControlSocket;
		ISocket::tRef mrDataSocket;
		
		Party::tRef mrParty;
		std::map<unsigned, Peer::tRef> mvAllPeers;
		State::tRef mrState;

		/// The control port
		Setting<tPort>::tCacheRef mrControlPortSetting;
		Setting<tPort>::tCacheRef mrDataPortSetting;

	public:
		PeerManager();
		~PeerManager();

		void LockParty(Peer * pPeer);
		void UnlockParty(Peer * pPeer);

		void PeerManager::ProcessMessage(const Packet<MessagePacket>::tRef &rPacket);

		/// Privileged commands
		static void ProcessCommand(const CommandPacket * pCmd);

		void Run();

		void OnControlSocketConnect();
		void OnData();

		// Properties
		inline const TcpSocket::tRef& getControlSocket() const {
			return mrControlSocket;
		};
		inline const ISocket::tRef& getDataSocket() const {
			return mrDataSocket;
		};

		inline Party::tRef getParty() const {
			return mrParty;
		};

		inline State::tRef getState() const {
			return mrState;
		};
		inline void setState(State::tRefa rState) {
			mrState = rState;
		};

		GC_AUTOSIZE(PeerManager);
	};


	/**
	 * Opens a UDP server port, and reads packets from it.
	 **/
	class UdpListener: public UdpSocket {
		public:
			explicit UdpListener(tPort port);
			~UdpListener();

			void OnDataRecv();
	};
};

#endif
